#include "histogram.h"
#include <QDebug>

Histogram1D::~Histogram1D()
{

}

// Computes the 1D histogram.
cv::MatND Histogram1D::getHistogram(const cv::Mat &image)
{
    cv::MatND hist;
    // Compute histogram
    cv::calcHist(&image,
        1, // histogram from 1 image only
        channels, // the channel used
        cv::Mat(), // no mask is used
        hist, // the resulting histogram
        1, // it is a 1D histogram
        histSize, // number of bins
        ranges // pixel value range
    );
    return hist;
}

cv::Mat Histogram1D::stretch(const cv::Mat &image, double percent)
{
    return stretch(image, percent, percent);
}

cv::Mat Histogram1D::stretch(const cv::Mat &image, double percent1, double percent2)
{

    cv::MatND hist = getHistogram(image);
    int imin, imax;
    if(percent1 < 0.0) percent1 = 0.0;
    if(percent1 > 1.0) percent1 = 1.0;
    percent1 = image.rows * image.cols * percent1;
    double value = 0;
    for(imin = 0; imin < histSize[0]; imin++)
    {
        value += hist.at<float>(imin);
        if(value > percent1) break;
    }

    value = 0;
    if(percent2 < 0.0) percent2 = 0.0;
    if(percent2 > 1.0) percent2 = 1.0;
    percent2 = image.rows * image.cols * percent2;
    for(imax = histSize[0] - 1; imax >= 0; imax--)
    {
        value += hist.at<float>(imax);
        if(value > percent2) break;
    }
    //int dim = 256;
    cv::Mat lookup(1, 256, CV_8U);

    for(int i = 0; i < 256; i++)
    {
        if(i < imin) lookup.at<uchar>(i) = 0;
        else if(i > imax) lookup.at<uchar>(i) = 255;
        else
        {
            int v = 255 * (i - imin) / (imax - imin);
            lookup.at<uchar>(i) = static_cast<uchar>(v);
        }
    }
    cv::Mat ret;
    cv::LUT(image, lookup, ret);
    return ret;
}

void Histogram1D::colorReduce(cv::Mat &image, int div=64)
{
    int nl = image.rows; // number of lines
    int nc = image.cols * image.channels();
    if (image.isContinuous())
    {
        // then no padded pixels
        nc = nc * nl;
        nl = 1; // it is now a 1D array
    }
    // this loop is executed only once
    // in case of continuous images
    for (int j = 0; j < nl; j++)
    {
        uchar* data = image.ptr<uchar>(j);
        for (int i = 0; i < nc; i++)
        {
            // process each pixel ---------------------
            data[i] = data[i] / div * div + div / 2;
            // end of pixel processing ----------------
        } // end of line
    }
}

cv::Mat Histogram1D::normalize(const cv::Mat &image)
{
    // Compute histogram first
    cv::MatND hist = getHistogram(image);
    int imin, imax;
    for(imin = 0; imin < histSize[0]; imin++)
    {
        if(hist.at<float>(imin) > 0) break;
    }

    for(imax = histSize[0] - 1; imax >= 0; imax--)
    {
        if(hist.at<float>(imax) > 0) break;
    }

    cv::Mat lookup(1, 256, CV_8U);

    for(int i = 0; i < 256; i++)
    {
        if(i < imin) lookup.at<uchar>(i) = 0;
        else if(i > imax) lookup.at<uchar>(i) = 255;
        else
        {
            int v = 255 * (i - imin) / (imax - imin);
            lookup.at<uchar>(i) = static_cast<uchar>(v);
        }
    }
    cv::Mat ret;
    cv::LUT(image, lookup, ret);
    return ret;
}

// Computes the 1D histogram and returns an image of it.
cv::Mat Histogram1D::getHistogramImage(const cv::Mat &image)
{
    // Compute histogram first
    cv::MatND hist = getHistogram(image);
    // Get min and max bin values
    double maxVal = 0;
    double minVal = 0;
    cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
    // Image on which to display histogram
    cv::Mat histImg(histSize[0], histSize[0], CV_8U, cv::Scalar(255));
    // set highest point at 90% of nbins
    int hpt = static_cast<int>(0.9 * histSize[0]);
    // Draw a vertical line for each bin
    for( int h = 0; h < histSize[0]; h++ )
    {
        float binVal = hist.at<float>(h);
        int intensity = static_cast<int>(binVal * hpt / maxVal);
        // This function draws a line between 2 points
        cv::line(histImg, cv::Point(h, histSize[0]),
        cv::Point(h,histSize[0]-intensity), cv::Scalar::all(0));
    }
    return histImg;
}


cv::MatND ColorHistogram::getHistogram(const cv::Mat &image)
{
    cv::MatND hist;
    // Compute histogram
    cv::calcHist(&image,
                 1, // histogram of 1 image only
                 channels, // the channel used
                 cv::Mat(), // no mask is used
                 hist, // the resulting histogram
                 3, // it is a 3D histogram
                 histSize, // number of bins
                 ranges // pixel value range
                 );
    return hist;
}

cv::SparseMat ColorHistogram::getSparseHistogram(const cv::Mat &image)
{
    cv::SparseMat hist(3,histSize,CV_32F);
    // Compute histogram
    cv::calcHist(&image,
                 1, // histogram of 1 image only
                 channels, // the channel used
                 cv::Mat(), // no mask is used
                 hist, // the resulting histogram
                 3, // it is a 3D histogram
                 histSize, // number of bins
                 ranges // pixel value range
                 );
    return hist;
}

void ColorHistogram::colorReduce(cv::Mat &image, int div=64)
{
    int nl = image.rows; // number of lines
    int nc = image.cols * image.channels();
    if (image.isContinuous())
    {
        // then no padded pixels
        nc = nc * nl;
        nl = 1; // it is now a 1D array
    }
    // this loop is executed only once
    // in case of continuous images
    for (int j = 0; j < nl; j++)
    {
        uchar* data = image.ptr<uchar>(j);
        for (int i = 0; i < nc; i++)
        {
            // process each pixel ---------------------
            data[i] = data[i] / div * div + div / 2;
            // end of pixel processing ----------------
        } // end of line
    }
}
